package com.jycode.commons.genericdaoDeprecated;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javax.persistence.NamedQuery;

import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.type.Type;
import org.springframework.orm.hibernate3.SessionFactoryUtils;

import com.jycode.commons.genericdao.finder.FinderArgumentTypeFactory;
import com.jycode.commons.genericdao.finder.FinderExecutor;
import com.jycode.commons.genericdao.finder.FinderNamingStrategy;
import com.jycode.commons.genericdao.finder.impl.SimpleFinderArgumentTypeFactory;
import com.jycode.commons.genericdao.finder.impl.SimpleFinderNamingStrategy;

/**
 * Hibernate implementation of GenericDao
 * A typesafe implementation of CRUD and finder methods based on Hibernate and Spring AOP
 * The finders are implemented through the executeFinder method. Normally called by the FinderIntroductionInterceptor
 * @deprecated
 */
@SuppressWarnings("unchecked")
public class GenericDaoHibernateImpl<T, PK extends Serializable> implements GenericDao<T, PK>, FinderExecutor
{
    private SessionFactory sessionFactory;
    private FinderNamingStrategy namingStrategy = new SimpleFinderNamingStrategy(); // Default. Can override in config
    private FinderArgumentTypeFactory argumentTypeFactory = new SimpleFinderArgumentTypeFactory(); // Default. Can override in config

    private Class<T> type;

    public GenericDaoHibernateImpl(Class<T> type)
    {
        this.type = type;
    }

    @SuppressWarnings("unchecked")
	public PK create(T o)
    {
        return (PK) getSession().save(o);
    }

    @SuppressWarnings("unchecked")
	public T read(PK id)
    {
        return (T) getSession().get(type, id);
    }

    public void update(T o)
    {
        getSession().update(o);
    }

    public void delete(T o)
    {
        getSession().delete(o);
    }

    @SuppressWarnings("unchecked")
	public List<T> executeFinder(Method method, final Object[] queryArgs)
    {
        final Query namedQuery = prepareQuery(method, queryArgs);
        return (List<T>)namedQuery.list();
    }

    @SuppressWarnings("unchecked")
	public Iterator<T> iterateFinder(Method method, final Object[] queryArgs)
    {
        final Query namedQuery = prepareQuery(method, queryArgs);
        if(namedQuery instanceof SQLQuery){
        	return (Iterator<T>) namedQuery.list().iterator();
        }
        return (Iterator<T>) namedQuery.iterate();
    }

    @SuppressWarnings("unchecked")
	public Object doScalarQueryFinder(Method method, final Object[] queryArgs)
    {
        final Query namedQuery = prepareQuery(method, queryArgs);
        Object res = namedQuery.uniqueResult();

        Class<?> c = method.getReturnType();
        if(c.equals(Boolean.class)||c.equals(boolean.class)){
        	//手工帮助完成结果到布尔类型的转换, 防止转换异常
        	if(res instanceof Long || res instanceof Integer || res instanceof Byte)
        	{
        		return (((Long)res)!=0);
        	}else if(res instanceof String){
        		String s = res.toString().toLowerCase();
        		return (s.equals("true") || s.equals("yes") || s.equals("t") || s.equals("y"));
        	}
        	return res!=null;
        }

        return res;
    }

//    public ScrollableResults scrollFinder(Method method, final Object[] queryArgs)
//    {
//        final Query namedQuery = prepareQuery(method, queryArgs);
//        return (ScrollableResults) namedQuery.scroll();
//    }

    private Query prepareQuery(Method method, Object[] queryArgs)
    {
        final String queryName = getNamingStrategy().queryNameFromMethod(type, method);
        Query namedQuery = null;
        try{
        	//首先尝试从基类BaseEntity的注解中查找全限定名的命名查询
        	Annotation nqs = type.getAnnotation(javax.persistence.NamedQueries.class);
        	if(type.getSuperclass()!=null){
        		nqs = type.getSuperclass().getAnnotation(javax.persistence.NamedQueries.class);
        	}
        	NamedQuery[] nqColl = (NamedQuery[])nqs.annotationType().getMethod("value").invoke(nqs);
        	for (NamedQuery nq : nqColl) {
        		if(nq.name().equals(queryName)){
        			namedQuery = getSession().createQuery(nq.query());
        			break;
        		}
        	}

        	//如果基类中没有定义该命名查询注解，则进入标准搜索算法中
        	if(namedQuery==null)
        		namedQuery = getSession().getNamedQuery(queryName);
        }catch(Exception ex){
        	// 如果不能找到注解中的命名查询,则尝试执行存储过程.
			StringBuffer sbStoreProcSql = new StringBuffer();

			//查找存储过程，形如“USER_findByName”
			sbStoreProcSql.append(queryName.replaceAll("\\.", "_"));

			//执行存储过程命令
			{
				StringBuffer sb1 = new StringBuffer();
				sb1.append("{ call ").append(
						sbStoreProcSql.toString()).append("(");
				for (int i = 0; i < queryArgs.length; i++) {
					sb1.append("?");
					if (queryArgs.length - 1 != i)
						sb1.append(",");
				}
				sb1.append(") }");
				try {
					namedQuery = getSession().createSQLQuery(sb1.toString());

					if(false){
						//返回带有列名的Hashmap值对照表
						namedQuery.setResultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP);
					}else if(false){
						//仅仅返回给出的下列字段及转换到指定的数据类型，忽略结果基中的其他字段
						SQLQuery q = ((SQLQuery)namedQuery);
						q.addScalar("ID", org.hibernate.Hibernate.LONG)
						.addScalar("FIRST_NAME")
						.addScalar("PASSWORD");
					}else{
						//返回Hibernate实体类类型
						((SQLQuery)namedQuery).addEntity(type);
					}
				} catch (Exception exx) {
				}
				// Note
				// 存储过程的直接调用语法如下
				// Connection con = null;
				// CallableStatement proc=null;
				// con = this.getSession().connection();
				// proc = con.prepareCall("{ call sp_new_game(?,?,?,?) }");
				// //此语法适用于MySQL/Oracle/DB2
				// proc.setString(1,login);
				// proc.setInt(2,tableId);
				// proc.setInt(3, 4);
				// proc.registerOutParameter(4, java.sql.Types.INTEGER);
				// // proc.registerOutParameter(5, java.sql.Types.VARCHAR);
				// proc.execute();
				// result=proc.getInt(4);
			}
        	//HibernateDaoSupport hds = new HibernateDaoSupport();
        	//hds.getHibernateTemplate().findByNamedQuery(queryName);
        }
        String[] namedParameters = namedQuery.getNamedParameters();
        if(namedParameters.length==0)
        {
            setPositionalParams(queryArgs, namedQuery);
        } else {
            setNamedParams(namedParameters, queryArgs, namedQuery);
        }
        return namedQuery;
    }

    private void setPositionalParams(Object[] queryArgs, Query namedQuery)
    {
        // Set parameter. Use custom Hibernate Type if necessary
        if(queryArgs!=null)
        {
            for(int i = 0; i < queryArgs.length; i++)
            {
                Object arg = queryArgs[i];
                Type argType = getArgumentTypeFactory().getArgumentType(arg);
                if(argType != null)
                {
                    namedQuery.setParameter(i, arg, argType);
                }
                else
                {
                    namedQuery.setParameter(i, arg);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
	private void setNamedParams(String[] namedParameters, Object[] queryArgs, Query namedQuery)
    {
        // Set parameter. Use custom Hibernate Type if necessary
        if(queryArgs!=null)
        {
            for(int i = 0; i < queryArgs.length; i++)
            {
                Object arg = queryArgs[i];
                Type argType = getArgumentTypeFactory().getArgumentType(arg);
                if(argType != null)
                {
                    namedQuery.setParameter(namedParameters[i], arg, argType);
                }
                else
                {
                    if(arg instanceof Collection) {
                        namedQuery.setParameterList(namedParameters[i], (Collection) arg);
                    }
                    else
                    {
                        namedQuery.setParameter(namedParameters[i], arg);
                    }
                }
            }
        }
    }

    public Session getSession()
    {
        boolean allowCreate = true;
        return SessionFactoryUtils.getSession(sessionFactory, allowCreate);
    }

    public void setSessionFactory(SessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
    }

    public FinderNamingStrategy getNamingStrategy()
    {
        return namingStrategy;
    }

    public void setNamingStrategy(FinderNamingStrategy namingStrategy)
    {
        this.namingStrategy = namingStrategy;
    }

    public FinderArgumentTypeFactory getArgumentTypeFactory()
    {
        return argumentTypeFactory;
    }

    public void setArgumentTypeFactory(FinderArgumentTypeFactory argumentTypeFactory)
    {
        this.argumentTypeFactory = argumentTypeFactory;
    }
}
